#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>

#define WORKER_NUMBER 2
#define CAPACITY 3

void find_file(char *path, char *target);

typedef struct {
	int is_end;
	char path[128];
	char string[128];
}task;

task buffer[CAPACITY];
int in;
int out;

int buffer_is_empty()
{
	return in == out;
}

int buffer_is_full()
{
	return (in + 1) % CAPACITY == out;
}

task get_item()
{
	task item;

	item = buffer[out];
	out = (out + 1) % CAPACITY;
	return item;
}

void put_item(task item)
{
	buffer[in] = item;
	in = (in + 1) % CAPACITY;
}

pthread_mutex_t mutex;
pthread_cond_t wait_empty_buffer;
pthread_cond_t wait_full_buffer;

void *worker_entry(void *arg)
{
	task task;
	while(1) {
    		pthread_mutex_lock(&mutex);
		while (buffer_is_empty())
			pthread_cond_wait(&wait_full_buffer, &mutex);

		task = get_item();
		pthread_cond_signal(&wait_empty_buffer);
		pthread_mutex_unlock(&mutex);

		if (task.is_end)
			break;

		find_file(task.path, task.string);
	}
	return NULL;
}

void find_file(char *path, char *target)
{
	FILE *file = fopen(path, "r");

	char line[256];
	while (fgets(line, sizeof(line), file)) {
		if(strstr(line, target))
			printf("%s: %s", path, line);
	}

	fclose(file);
}

void *find_dir(char *path, char *target)
{
	DIR *dir = opendir(path);
	struct dirent *entry;
	while (entry = readdir(dir)) {
		if (strcmp(entry->d_name, ".") == 0)
			continue;

		if (strcmp(entry->d_name, "..") == 0)
			continue;

		char *new_path;
		char str[2] = "/";
		new_path = (char *)malloc(sizeof(char) * 50);
		strcpy(new_path, path);
		strcat(new_path, str);
		strcat(new_path, entry->d_name);

		if (entry->d_type == DT_DIR) 
			find_dir(new_path, target);

		if (entry->d_type == DT_REG) {
			pthread_mutex_lock(&mutex);
			while (buffer_is_full())
				pthread_cond_wait(&wait_empty_buffer, &mutex);

			task task;
			task.is_end = 0;
			strcpy(task.path, new_path);
			strcpy(task.string, target);
			put_item(task);

			pthread_cond_signal(&wait_full_buffer);
			pthread_mutex_unlock(&mutex);
		}
	}
}

int main(int argc, char *argv[])
{
	if ( argc != 3) {
		puts("Usage: pfind file string");
		return 0;
	}

	char *path = argv[1];
	char *string = argv[2];
	
	struct stat info;
	stat(path, &info);

	if (S_ISDIR(info.st_mode)) {
		pthread_t worker_tid;
		pthread_t workers_tid;

		pthread_mutex_init(&mutex, NULL);
		pthread_cond_init(&wait_empty_buffer, NULL);
		pthread_cond_init(&wait_full_buffer, NULL);

		pthread_create(&worker_tid, NULL, worker_entry, NULL);
		pthread_create(&workers_tid, NULL, worker_entry, NULL);

		find_dir(path, string);

		task task;
		task.is_end = 1;
 		int i;
		for (i = 0; i < 2; ++i) {
			pthread_mutex_lock(&mutex);
			while (buffer_is_full()) {
				pthread_cond_wait(&wait_empty_buffer, &mutex);
			}
			put_item(task);

			pthread_cond_signal(&wait_full_buffer);
			pthread_mutex_unlock(&mutex);
		}
		pthread_join(worker_tid, NULL);
		pthread_join(workers_tid, NULL);

	} else
		find_file(path, string);
	return 0;

}
